package com.uxsino.simo.indicator.expression.mvel;

import java.io.Serializable;
import java.util.Map;

import org.mvel2.CompileException;
import org.mvel2.MVEL;
import org.mvel2.ParserContext;
import org.mvel2.PropertyAccessException;
import org.mvel2.integration.VariableResolverFactory;
import org.mvel2.integration.impl.MapVariableResolverFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.uxsino.simo.indicator.IExprItem;
import com.uxsino.simo.indicator.expression.ExprEvaluator;

public final class ExprEvaluatorMVEL extends ExprEvaluator {
    private static Logger logger = LoggerFactory.getLogger("ExprEvaluator");

    private ParserContext parserCtxt = new ParserContext();

    private NEDepoVariableFactory depoVariableFactory = new NEDepoVariableFactory();

    public ExprEvaluatorMVEL() {
        super();
        parserCtxt.addPackageImport("Math");
    }

    @Override
    public Object evalExpr(Map<String, Object> currentItem, String expr) {
        Serializable compiled;
        // test
        try {
            ParserContext pctxt = ParserContext.create();
            compiled = MVEL.compileExpression(expr, pctxt);
        } catch (CompileException ce) {
            String errorExpr = new String(ce.getExpr());
            logger.error("compile error in expression {}\n.{}", errorExpr, ce);
            throw ce;
        } catch (Exception e) {
            logger.error("expr error!{}\n.{}", expr, e);
            throw e;
        }

        return evalCompiledExpr(currentItem, compiled);
    }

    @Override
    public Object evalCompiledExpr(Map<String, Object> currentItem, Object compiled) {
        VariableResolverFactory variableFactory = null;
        if (this.depo != null) {
            depoVariableFactory.setDepo(this.depo);
            variableFactory = depoVariableFactory;
        }
        if (currentItem != null) {
            MapVariableResolverFactory mf = new MapVariableResolverFactory(currentItem);
            // mf.setNextFactory(depoVariableFactory);
            variableFactory = mf;
        }
        try {
            // return null == variableFactory ? MVEL.eval(expr) : MVEL.eval(expr, variableFactory);
            return null == variableFactory ? MVEL.executeExpression(compiled)
                    : MVEL.executeExpression(compiled, variableFactory);
        } catch (PropertyAccessException pae) {
            String errorExpr = new String(pae.getExpr());
            logger.error("variable not found in expression {}\n.{}", errorExpr, pae);
            return null;
        } catch (CompileException ce) {
            String errorExpr = new String(ce.getExpr());
            logger.error("compile error in expression {}\n.{}", errorExpr, ce);
            return null;
        }

    }

    @Override
    public void compileExprItem(IExprItem item) {
        ParserContext pctxt = ParserContext.create();

        item.setCompiled(MVEL.compileExpression(item.getFormula(), pctxt));

        item.setReferredVariables(pctxt.getInputs());
    }

}
